/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "CameraParallel.h"
#include <xml/Stream.h>
#include <xml/Saver.h>
#include <Converter.h>
#include <string>
#include <lib3d/Math_lib3d.h>
#include <Gluck.h>
#include <safe_new.h>

#include <boost/bind.hpp>

namespace liba {
namespace lib3d {
namespace node {

const Atom CameraParallel::node_name( "CameraParallel" );
const Atom CameraParallel::width_name( "Width" );
const Atom CameraParallel::height_name( "Height" );


CameraParallel::CameraParallel(const Atom & name, Node * parent )
	: Camera( name, parent )
	, width( 1 )
	, height( 1 )
{
	this->width.Changed().connect(boost::bind(&CameraParallel::WidthChanged, this, _1));
	this->RegisterProperty(width_name, &this->width);
	this->height.Changed().connect(boost::bind(&CameraParallel::HeightChanged, this, _1));
	this->RegisterProperty(height_name, &this->height);
	node_type = node_name;
}

CameraParallel::CameraParallel(const CameraParallel& rhs)
	: Camera(rhs)
	, width(rhs.width)
	, height(rhs.height)
{
	this->width.Changed().connect(boost::bind(&CameraParallel::WidthChanged, this, _1));
	this->RegisterProperty(width_name, &this->width);
	this->height.Changed().connect(boost::bind(&CameraParallel::HeightChanged, this, _1));
	this->RegisterProperty(height_name, &this->height);
}

CameraParallel * CameraParallel::Clone()
{
	return new CameraParallel(*this);
}

void CameraParallel::WidthChanged(double value)
{
	on_change_parameters();
}

void CameraParallel::HeightChanged(double value)
{
	on_change_parameters();
}

bool CameraParallel::on_attribute( xml::Provider * prov, const std::string & name, const std::string & value )
{
	if ( name == "Height" )
		return this->height.FromString(value);
	if ( name == "Width" )
		return this->width.FromString(value);
	return Camera::on_attribute( prov, name, value );
}

void CameraParallel::on_save_attributes( xml::SaverBase * saver )const
{
	if(height.Get() != 1)
		saver->save_attribute("Height", height.Get());

	if(width.Get() != 1)
		saver->save_attribute("Width", width.Get());

	Camera::on_save_attributes(saver);
}

void CameraParallel::on_change_parameters()
{
	/** @todo calculate frustrum */
	desc.projection = calculate_orthogonal_projection_matrix( -width/2, width/2, -height/2, height/2, desc.near_plane, desc.far_plane );
	desc.frustrum = math::Frustrum( desc.near_plane, desc.far_plane, -width/2, width/2, -height/2, height/2);
}

Ray CameraParallel::get_world_ray(double ray_x, double ray_y)
{
	THROW(Gluck(__FUNCTION__,"Not implemented yet"));
	return Ray();
}

} // namespace node
} // namespace lib3d
} // namespace liba

